package com.jianbo.proxy;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.smartboot.http.common.enums.HttpStatus;
import org.smartboot.http.common.logging.Logger;
import org.smartboot.http.common.logging.LoggerFactory;
import org.smartboot.http.server.HttpBootstrap;
import org.smartboot.http.server.HttpRequest;
import org.smartboot.http.server.HttpResponse;
import org.smartboot.http.server.HttpServerHandler;

import com.jianbo.proxy.conf.ArgsConf;
import com.jianbo.proxy.conf.ConfBuilder;
import com.jianbo.proxy.conf.PropertiesConf;
import com.jianbo.proxy.conf.jsconfig.ApiConfig;
import com.jianbo.proxy.conf.jsconfig.CacheConfig;
import com.jianbo.proxy.conf.jsconfig.FilterConfig;
import com.jianbo.proxy.conf.jsconfig.ProxyConfig;
import com.jianbo.proxy.conf.jsconfig.StaticWebConf;
import com.jianbo.proxy.core.ApiWeb;
import com.jianbo.proxy.core.CacheWeb;
import com.jianbo.proxy.core.ErrorGlobalHandler;
import com.jianbo.proxy.core.FilterWeb;
import com.jianbo.proxy.core.PresetApiWeb;
import com.jianbo.proxy.core.ProxyWeb;
import com.jianbo.proxy.core.StaticWeb;
import com.jianbo.proxy.entity.HttpResponseMiddle;
import com.jianbo.proxy.page.Page404;
import com.jianbo.proxy.util.ArgsLog;
import com.jianbo.proxy.util.Context;
import com.jianbo.proxy.util.Debug;
import com.jianbo.proxy.util.TimeUse;

public class Start {
    
    private static Logger logger = LoggerFactory.getLogger(Start.class);

    public static int port;

    public static void main(String[] args) {
        try {
            start(args);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } 
    }
    
    public static void start(String[] args) throws InterruptedException, ExecutionException {
        Date start = new Date();
        ArgsLog.info(args);

        // 参数配置
        ArgsConf argsConf = ArgsConf.getInstance(args);
        // 属性配置
        PropertiesConf propConf = PropertiesConf.getInstance(argsConf);

        // TODO 合并参数和属性配置
        Integer port = argsConf.getPort();
        if(port == null) {
            port = propConf.getPort();
            logger.info("start server propConf.getPort: " + port);
        } else {
            logger.info("start server argsConf.getPort: " + port);
        }
        Start.port = port;

        TimeUse.start("checkConfig");
        ConfBuilder.checkConfigFile();
        TimeUse.end("checkConfig");

        TimeUse.start("loadConf");
        // 配置异步加载
        CompletableFuture<StaticWebConf> staticWebConfFuture = CompletableFuture.supplyAsync(() -> {
            TimeUse.start("getStaticConf");
            StaticWebConf conf = StaticWebConf.getInstance();
            TimeUse.end("getStaticConf");
            return conf;
        });
        
        CompletableFuture<ProxyConfig> proxyConfFuture = CompletableFuture.supplyAsync(() -> {
            TimeUse.start("getProxyConf");
            ProxyConfig conf = ConfBuilder.getProxyConf();
            TimeUse.end("getProxyConf");
            return conf;
        });
        
        CompletableFuture<FilterConfig> filterConfigFuture = CompletableFuture.supplyAsync(() -> {
            TimeUse.start("getFilterConf");
            FilterConfig conf = ConfBuilder.getFilterConfig();
            TimeUse.end("getFilterConf");
            return conf;
        });
        
        CompletableFuture<CacheConfig> cacheConfigFuture = CompletableFuture.supplyAsync(() -> {
            TimeUse.start("getCacheConf");
            CacheConfig conf = ConfBuilder.getCacheConfig();
            TimeUse.end("getCacheConf");
            return conf;
        });
        
        CompletableFuture<ApiConfig> apiConfigFuture = CompletableFuture.supplyAsync(() -> {
            TimeUse.start("getApiConf");
            ApiConfig conf = ConfBuilder.getApiConfig();
            TimeUse.end("getApiConf");
            return conf;
        });

        // 等待所有配置加载完成
        StaticWebConf staticWebConf = staticWebConfFuture.get();
        ProxyConfig proxyConf = proxyConfFuture.get();
        FilterConfig filterConfig = filterConfigFuture.get();
        CacheConfig cacheConfig = cacheConfigFuture.get();
        ApiConfig apiConfig = apiConfigFuture.get();
        
        // 设置配置到对应组件
        final StaticWeb staticWeb = StaticWeb.getInstance();
        final ProxyWeb proxyWeb = Context.put(new ProxyWeb());
        final FilterWeb filterWeb = Context.put(new FilterWeb());
        final CacheWeb cacheWeb = Context.put(new CacheWeb());
        final ApiWeb apiWeb = Context.put(new ApiWeb());
        final PresetApiWeb presetApiWeb = Context.put(new PresetApiWeb());
        
        cacheWeb.setCacheConf(cacheConfig);
        proxyWeb.setProxyConf(proxyConf);
        filterWeb.setFilterConf(filterConfig);
        apiWeb.setApiConf(apiConfig);
            
        TimeUse.end("loadConf");

        HttpBootstrap bootstrap = new HttpBootstrap();
        bootstrap.configuration().debug(false);

        Map<String, HttpResponseMiddle> memoryCache = new HashMap<>();
        TimeUse.start("bootstrap.httpHandler");
        bootstrap.httpHandler(new HttpServerHandler() {
            public void handle(HttpRequest request, HttpResponse response) throws Throwable {
                String path = request.getRequestURI();
                // Debug.info("RequestURI", path);
                try {

                    // 根据request读取目录资源
                    // 从内存中读取缓存
                    if(propConf.staticCache()) {
                        if(memoryCache.containsKey(path)) {
                            Debug.info("memory cache path: " + path);
                            HttpResponseMiddle middle = memoryCache.get(path);
                            middle.flush(response);
                            return;
                        } else {
                            Debug.info("no cache path: " + path);
                        }
                    }

                    HttpResponseMiddle middle = new HttpResponseMiddle();
                    
                    boolean match = false;
                    boolean proxy = false;
                    boolean statics = false;
                    
                    // 优先级 cache -> proxy -> presetApi -> api -> static

                    // cache匹配
                    if(cacheWeb.handle(path, request, middle)) {
                        match = true;
                        statics = true; // 能从缓存读取就是静态资源
                    
                    } else if(proxyWeb.handle(path, request, middle)) {
                        // 匹配成功
                        match = true;
                        proxy = true;
                    } else if(presetApiWeb.handle(path, request, middle)) { 
                        match = true;
                    } else if(apiWeb.handle(path, request, middle)) {
                        match = true;
    
                    } else if(staticWeb.handle(path, request, middle)) {
                        // 匹配成功
                        match = true;
                        statics = true;
                    }

                    
                    // 过滤器
                    if(match == true) {
                        // 匹配成功才需要走过滤器
                        if(proxy == false) {
                            // 代理过的请求不需要走过滤器
                            filterWeb.handle(path, request, middle);
                        }
                    }

                    // 匹配成功，写入middle到response
                    if(match == true) {
                        middle.flush(response);
                    }


                    // 写入缓存到内存当中
                    if(statics && propConf.staticCache()) {
                        // 保存middle
                        memoryCache.put(path, middle);
                    }
                    
                    if(match == false) {
                        // 404
                        response.setHttpStatus(HttpStatus.NOT_FOUND);
                        Page404.flush(response);

                    }
                } catch (RuntimeException e) {
                    logger.error(e.getMessage(), e);
                    String msg = e.getMessage();
                    if("proxyError".equals(msg)) {
                        proxyWeb.error(response);
                        return;
                    }
                    ErrorGlobalHandler.response(e, request, response);
                }
            }
        }).setPort(port).start();
        Date end = new Date();
        TimeUse.end("bootstrap.httpHandler");
        TimeUse.logMs();
        logger.info("Start Http Server By " + port + " cost: " + (end.getTime() - start.getTime()) + "ms");
    }
}
